/*
 * acooly.cn Inc.
 * Copyright (c) 2016 All Rights Reserved.
 * create by zhangpu
 * date:2016年4月4日
 *
 */
package com.acooly.module.openapi.client.provider.fudian.marshall;

import com.acooly.core.utils.Strings;
import com.acooly.core.utils.validate.Validators;
import com.acooly.module.openapi.client.api.exception.ApiClientException;
import com.acooly.module.openapi.client.api.message.MessageFactory;
import com.acooly.module.openapi.client.api.util.Jsons;
import com.acooly.module.openapi.client.provider.fudian.FudianConstants;
import com.acooly.module.openapi.client.provider.fudian.OpenAPIClientFudianProperties;
import com.acooly.module.openapi.client.provider.fudian.domain.*;
import com.acooly.module.openapi.client.provider.fudian.enums.ApiServiceType;
import com.acooly.module.openapi.client.provider.fudian.exception.FudianProcessingException;
import com.acooly.module.safety.Safes;
import com.acooly.module.safety.key.KeyLoadManager;
import com.acooly.module.safety.signature.SignTypeEnum;
import com.acooly.module.safety.support.CodecEnum;
import com.acooly.module.safety.support.KeyStoreInfo;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.Feature;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;

import javax.annotation.Resource;
import java.util.Map;

/**
 * @author zhangpu
 */
@Slf4j
public class FudianAbstractMarshall {

    @Autowired
    protected OpenAPIClientFudianProperties openAPIClientFudianProperties;

    @Autowired
    protected KeyLoadManager keyStoreLoadManager;

    @Resource(name = "fudianMessageFactory")
    private MessageFactory messageFactory;


    protected String doMarshall(FudianRequest source) {
        // pre
        source.setPartner(getProperties().getMerchantNo());
        source.setService(FudianConstants.getServiceName(source));
        source.setMerchantNo(source.getPartner());
        settingNotifyUrl(source);

        doVerifyParam(source);

        String reqData = Jsons.toJson(source);
        log.info("请求报文数据: {}", reqData);
        Map<String, String> datas = Maps.newHashMap();
        datas.put(FudianConstants.SIGN, doSign(reqData));
        datas.put(FudianConstants.DATA, reqData);
        datas.put(FudianConstants.CERT_INFO, getKeyStore().getCertificateInfo(CodecEnum.HEX));
        datas.put(FudianConstants.MERCHANT_NO, getProperties().getMerchantNo());
        String message = Jsons.toJson(datas);
        log.debug("请求报文体: {}", message);
        return message;
    }


    protected FudianResponse doUnMarshall(String message, String serviceName, boolean notify) {
        try {
            log.info("响应报文:{}", message);
            JSONObject jsonObject = JSONObject.parseObject(message, Feature.OrderedField);
            String content = jsonObject.getString(FudianConstants.RESPONSE_BODY_KEY);
            String sign = jsonObject.getString(FudianConstants.SIGN);
            //验签
            doVerify(content, sign);
            FudianResponse fudianResponse = null;
            if (notify) {
                fudianResponse = (FudianResponse) messageFactory.getNotify(serviceName);
            } else {
                fudianResponse = (FudianResponse) messageFactory.getResponse(serviceName);
            }
            fudianResponse = Jsons.parse(content, fudianResponse.getClass());
            FudianResponseWrap fudianResponseWrap = Jsons.parse(message, FudianResponseWrap.class);
            fudianResponse.setService(serviceName);
            fudianResponse.setPartner(fudianResponseWrap.getMerchantNo());
            fudianResponse.setRetCode(fudianResponseWrap.getRetCode());
            fudianResponse.setRetMsg(fudianResponseWrap.getRetMsg());
            return fudianResponse;
        } catch (Exception e) {
            throw new FudianProcessingException("解析报文错误:" + e.getMessage());
        }
    }

    public void settingNotifyUrl(FudianRequest apiMessage) {
        FudianApiMsg fudianApiMsg = FudianConstants.getApiMsgInfo(apiMessage);
        if (Strings.isBlank(apiMessage.getNotifyUrl()) && fudianApiMsg.service().getApiServiceType() != ApiServiceType.SYNC) {
            apiMessage.setNotifyUrl(FudianConstants.getCanonicalUrl(openAPIClientFudianProperties.getDomain(),
                    openAPIClientFudianProperties.getNotifyUrl()
                            + fudianApiMsg.service().code()));
        }
        if (fudianApiMsg.service().getApiServiceType() == ApiServiceType.REDIRECT && Strings.isBlank(apiMessage.getReturnUrl())) {
            throw new RuntimeException("跳转接口returnUrl是必须的");
        }
    }

    protected OpenAPIClientFudianProperties getProperties() {
        return openAPIClientFudianProperties;
    }


    protected void doVerifyParam(FudianMessage source) {
        try {
            source.doCheck();
            Validators.assertJSR303(source);
        } catch (Exception e) {
            throw new ApiClientException(e.getMessage());
        }
    }

    protected String doSign(String waitForSign) {
        return Safes.getSigner(SignTypeEnum.Cert).sign(waitForSign, getKeyStore());
    }

    protected void doVerify(String plain, String sign) {
        try {
            Safes.getSigner(SignTypeEnum.Cert).verify(plain, getKeyStore(), sign);
            log.info("验签成功");
        }catch (Exception e) {
            throw new ApiClientException("验签失败："+e.getMessage());
        }
    }

    protected KeyStoreInfo getKeyStore() {
        return keyStoreLoadManager.load(FudianConstants.PROVIDER_DEF_PRINCIPAL, FudianConstants.PROVIDER_NAME);
    }


}
